#include <ostream>
extern "C" {
#include <fcntl.h>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
}
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

// 去除字符串首尾的空白字符
std::string_view trim(const std::string_view &str) {
  auto start = std::find_if_not(str.begin(), str.end(), ::isspace);
  auto end = std::find_if_not(str.rbegin(), str.rend(), ::isspace).base();

  if (start == str.end()) {
    return ""; // 全部是空白字符
  }

  return str.substr(start - str.begin(), end - start);
}
std::vector<std::string_view> trim_all(std::vector<std::string_view> &strs) {
  std::vector<std::string_view> result;
  for (auto &str : strs) {
    auto trimmed = trim(str);
    if (!trimmed.empty()) {
      result.push_back(trimmed);
    }
  }
  return result;
}

// 按空格划分字符串, 并去除长度为 0 的部分
std::vector<std::string_view> split_by_space(const std::string_view &str) {
  std::vector<std::string_view> tokens;
  auto it = str.begin();
  auto end = str.end();

  while (it != end) {
    // 跳过前导空白字符
    it = std::find_if_not(it, end, ::isspace);
    if (it == end)
      break;

    // 找到下一个空白字符
    auto next = std::find_if(it, end, ::isspace);

    // 添加子字符串到结果
    tokens.push_back(str.substr(it - str.begin(), next - it));

    // 更新迭代器位置
    it = next;
  }

  return tokens;
}

// 从后往前搜 对字符串split 以便 xx&&& 这种情况可以分割为 xx& &&
std::vector<std::string_view> split_by(std::string_view str,
                                       std::string_view delimiter) {
  std::vector<std::string_view> result;
  size_t end = str.size();
  size_t start = str.rfind(delimiter, end);

  while (start != std::string_view::npos) {
    if (start + delimiter.size() < end) {
      result.push_back(trim(str.substr(start + delimiter.size(),
                                       end - (start + delimiter.size()))));
    }
    end = start;
    start = str.rfind(delimiter, end - 1);
  }

  // Add the first segment
  if (end > 0) {
    result.push_back(trim(str.substr(0, end)));
  }

  return result;
}

class RedirectStatement {
  enum class RedirectType {
    STDIN,
    STDOUT_APPEND,
    STDOUT_TRUNCATE,
    STDERR_APPEND,
    STDERR_TRUNCATE,
    OUTERR_APPEND,
    OUTERR_TRUNCATE

  };
  struct Redirect {
    std::string file_name;
    RedirectType type;
  };
  std::vector<Redirect> associated_files;
  std::vector<std::string> statement_tok;

public:
  RedirectStatement &operator=(RedirectStatement &) = default;
  RedirectStatement() {}
  void parse(std::string_view input) {
    size_t start = 0;
    size_t right_pos = input.size();
    while ((start = input.rfind("<>", start)) != std::string_view::npos) {
      auto part = trim(input.substr(start + 1, right_pos - start - 1));
      auto part_string = std::string(part);
      if (input[start] == '<') {
        associated_files.push_back({part_string, RedirectType::STDIN});
        right_pos = start;
      } else {
        if (start <= 0) {
          continue; // Invalid position, skip
        }
        auto modifier = input[start - 1];
        bool should_truncate = false;
        right_pos = start - 1;
        if (modifier == '>') {
          should_truncate = true;
          if (start >= 2) {
            modifier = input[start - 2];
          }
        }
        switch (modifier) {
        case '1':
          if (should_truncate) {
            associated_files.push_back(
                {part_string, RedirectType::STDOUT_TRUNCATE});
          } else {
            associated_files.push_back(
                {part_string, RedirectType::STDOUT_APPEND});
          }
          break; // STDOUT
        case '2':
          if (should_truncate) {
            associated_files.push_back(
                {part_string, RedirectType::STDERR_TRUNCATE});
          } else {
            associated_files.push_back(
                {part_string, RedirectType::STDERR_APPEND});
          }
          break; // STDERR
        case '&':
          if (should_truncate) {
            associated_files.push_back(
                {part_string, RedirectType::OUTERR_TRUNCATE});
          } else {
            associated_files.push_back(
                {part_string, RedirectType::OUTERR_APPEND});
          }
          break; // Both
        default:
          break; // Unknown
        }
        right_pos -= 1;
      }
    }

    auto tokens = split_by_space(input.substr(0, right_pos));
    statement_tok.reserve(tokens.size());
    transform(tokens.begin(), tokens.end(), std::back_inserter(statement_tok),
              [](std::string_view s) { return std::string(s); });
  }
  friend std::ostream &operator<<(std::ostream &os, const RedirectStatement &s);
};

std::ostream &operator<<(std::ostream &os, const RedirectStatement &s) {
  std::ostream_iterator<std::string> out_iter(os, " ");
  copy(s.statement_tok.cbegin(), s.statement_tok.cend(), out_iter);
  for (auto file : s.associated_files) {
    switch (file.type) {
    case RedirectStatement::RedirectType::STDIN:
      os << "<";
      break;
    case RedirectStatement::RedirectType::STDOUT_APPEND:
      os << ">>";
      break;
    case RedirectStatement::RedirectType::STDOUT_TRUNCATE:
      os << ">";
      break;
    case RedirectStatement::RedirectType::STDERR_APPEND:
      os << "2>>";
      break;
    case RedirectStatement::RedirectType::STDERR_TRUNCATE:
      os << "2>";
      break;
    case RedirectStatement::RedirectType::OUTERR_APPEND:
      os << "&>>";
      break;
    case RedirectStatement::RedirectType::OUTERR_TRUNCATE:
      os << "&>";
      break;
    default:
      os << "?";
      break;
      std::cout << file.file_name << " ";
    }
  }
  return os;
}

class BackStatement {
  RedirectStatement stmts;
  bool backend_run = false;

public:
  BackStatement &operator=(BackStatement &) = default;
  BackStatement(std::string_view input) {
    auto trimed = trim(input);
    if (trimed.back() == '&') {
      backend_run = true;
      stmts.parse(trimed.substr(0, trimed.size() - 1));
    }
  }
  friend std::ostream &operator<<(std::ostream &os, const BackStatement &s);
};
std::ostream &operator<<(std::ostream &os, const BackStatement &s) {
  os << s.stmts;
  if (s.backend_run) {
    os << " &";
  } else {
    os << " ";
  }
  return os;
}

// enum class JointSign { AND, OR, PIPE, SEMICOLON, BACKGROUND };
class LogicAndStatement {
  std::vector<BackStatement> stmts;

public:
  LogicAndStatement &operator=(LogicAndStatement &) = default;
  LogicAndStatement(std::string_view input) {
    auto parts = split_by(input, "&&");
    for (auto part : parts) {
      stmts.emplace_back(part);
    }
  }
  friend std::ostream &operator<<(std::ostream &os, const LogicAndStatement &s);
};

std::ostream &operator<<(std::ostream &os, const LogicAndStatement &s) {
  auto it = s.stmts.begin();
  std::cout << *it;
  for (++it; it != s.stmts.end(); ++it) {
    std::cout << " && " << *it;
  }
  return os;
}

class LogicOrStatement {
  std::vector<LogicAndStatement> stmts;

public:
  LogicOrStatement &operator=(LogicOrStatement &) = default;
  LogicOrStatement(std::string_view input) {
    auto parts = split_by(input, "||");
    for (auto part : parts) {
      stmts.emplace_back(part);
    }
  }
  friend std::ostream &operator<<(std::ostream &os, const LogicOrStatement &s);
};

std::ostream &operator<<(std::ostream &os, const LogicOrStatement &s) {
  auto it = s.stmts.begin();
  std::cout << *it;
  for (++it; it != s.stmts.end(); ++it) {
    std::cout << " || " << *it;
  }
  // std::ostream_iterator<LogicOrStatement> out_iter(std::cout, "||");
  // copy(s.stmts.cbegin(), s.stmts.cend(), out_iter);
  return os;
}
class Statement {
  std::vector<LogicOrStatement> stmts;

public:
  Statement &operator=(Statement &) = default;
  Statement(std::string_view input) {
    auto parts = split_by(input, ";");
    for (auto part : parts) {
      stmts.emplace_back(input);
    }
  }
  friend std::ostream &operator<<(std::ostream &os, const Statement &s);
};
std::ostream &operator<<(std::ostream &os, const Statement &s) {
  for (const auto &stmt : s.stmts) {
    // Print each statement
    // This is a placeholder, actual printing logic would go here
    std::cout << stmt << ';' << std::endl;
  }
  return os;
}

int main(int argc, char *argv[]) {
  // Statement k("ls && echo how are you >> need.txt");
  Statement k("ls");
  std::cout << k << std::endl;
  return 0;
}
